// 1 RS
// 2 Enable
// 4 CS1
// 8 CS2
// 16 Reset
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "arial_bold_14.h"
#include "font5x8.h"

static unsigned char PosX=0;
static unsigned char PosY=0;

#define KS0108_COMMAND 1
#define KS0108_ENABLE  2
#define KS0108_CS1     4
#define KS0108_CS2     8

/*

 on board female connector
 ----------------
|  1  3  5  7  - |
|                |
|  0  2  4  6  + |
 -----      -----
      |____|    

*/


void KS0108_Enable()
{
	PORTC |= KS0108_ENABLE;
	_delay_us(10);
	PORTC &= ~KS0108_ENABLE;
}


void KS0108_Command( unsigned cmd)
{
	PORTA = cmd;
	PORTC &= ~KS0108_COMMAND;
	KS0108_Enable();
}


void KS0108_Data( unsigned cmd)
{
	PORTA = cmd;
	PORTC |= KS0108_COMMAND;
	KS0108_Enable();
}

unsigned char sAddr = 0;

void KS0108_SetAddr(unsigned char x, unsigned char y)
{
	sAddr = x;
	PORTC |= KS0108_CS1 + KS0108_CS2;
	KS0108_Command( 0xB8 + y );    // row

	if ( x <64  )
	{
		KS0108_Command( 0x40 + 0 );  //set column
		PORTC &= ~KS0108_CS2;
	}
	else
	{
		PORTC &= ~KS0108_CS1;

		x-=64;
	}

	KS0108_Command( 0x40 + x );  //set column
}

void KS0108_Locate(unsigned char x, unsigned char y)
{
	PosX = x;
	PosY = y;
	KS0108_SetAddr( x*6, y);
}



void KS0108_Init()
{
	DDRA = 0xff;
	DDRB = 0xff;
	DDRC = 0xff;
	DDRD = 0xff;
	
	//init
	PORTA = 0;
	PORTC = 0;


	KS0108_Locate( 0,0 );

	PORTC |= KS0108_CS1+KS0108_CS2;
	KS0108_Command( 0xc0 ); // set "start line" to zero
	KS0108_Command( 0x3f ); // enable display

/*
	PORTA |= 1;
	for(char i=0;i<20;i++)
	{
		PORTC = 0xaa; 
		KS0108_Enable();
		PORTC = 0x55; 
		KS0108_Enable();
	}
*/

}


void KS0108_PrintChar5x8( char c )
{
	unsigned char *data = &font5x8[ (c -' ') * 5];

	unsigned char p = PosX * 6;

	for(int i=0;i<6;i++)
	{
		if ( p <64 )
		{
			PORTC |=  KS0108_CS1;
			PORTC &= ~KS0108_CS2;
		}
		else
		{
			PORTC &= ~KS0108_CS1;
			PORTC |=  KS0108_CS2;
		}


		//KS0108_Command( 0xB8 + PosY );

		if (i < 5)
		{
			KS0108_Data( pgm_read_word(data++) );
		}
		else
		{
			KS0108_Data( 0 ); 
		}

		p++;
	}

	PosX++;
}

void KS0108_PrintChar( char c )
{
	KS0108_PrintChar5x8( c );
}


unsigned char *pFont8x8 = 0;


void KS0108_SetFont( unsigned char *pF )
{
	pFont8x8 = pF;
}

void KS0108_PrintChar8x8( unsigned char c )
{
	unsigned char *pFont = &pFont8x8[ (c - ' ')*8 ];

	for(int i=0;i<8;i++)
	{
		if ( sAddr <64 )
		{
			PORTC |=  KS0108_CS1;
			PORTC &= ~KS0108_CS2;
		}
		else
		{
			PORTC &= ~KS0108_CS1;
			PORTC |=  KS0108_CS2;
		}

		KS0108_Data( pgm_read_word(pFont++) );

		sAddr++;
	}
}


void KS0108_RenderBitmap( unsigned char *data)
{

	for(int y=0;y<8;y++)
	{
		PORTC |= KS0108_CS1 + KS0108_CS2;

		PORTC |=  KS0108_CS1;
		PORTC &= ~KS0108_CS2;
		KS0108_SetAddr( 0,y );

		for(int x=0;x<64;x++)
		{
			KS0108_Data( pgm_read_word( data++ ) );
		}

		PORTC |=  KS0108_CS2;
		PORTC &= ~KS0108_CS1;
		KS0108_SetAddr( 64,y );

		for(int x=0;x<64;x++)
		{
			KS0108_Data( pgm_read_word( data++ ) );
		}
	}

}


void KS0108_RenderArray( unsigned char xx, unsigned char yy, unsigned char length, unsigned char *data)
{
	
	if ( ( xx < 64 ) && ((xx + length) > 64) )
	{
		KS0108_SetAddr( xx,yy );
		for( unsigned char x = 0; x < (64 - xx) ; x++ )
		{
			KS0108_Data( *data++ );
		}

		KS0108_SetAddr( 64,yy );
		for( unsigned char x = 0; x < (xx + length - 64 ) ; x++ )
		{
			KS0108_Data( *data++ );
		}

	}
	else
	{
		KS0108_SetAddr( xx,yy );
		for( unsigned char x = 0; x < length; x++ )
		{
			KS0108_Data( *data++ );
		}
	}

}


void KS0108_PrintArray( char x, char y, unsigned char *font, unsigned char **Map)
{
	char xx = x>>3;	
	x = x&0x7;
	char yy = y>>3;	
	y = y&0x7;

	for(int i=yy;i<yy+8;i++)
	{
		unsigned char c;
		unsigned char *t,*tt;

		KS0108_Locate( 0,i-yy );

		unsigned char *d = Map+ i*10 ;
		unsigned char *dd = d+10;

		t  =  &font[  (d[xx] - ' ') * 8 + x ];
		tt =  &font[ (dd[xx] - ' ') * 8 + x ];

		for(int j=xx;j<8;j++)
		{
			for ( char jj=x;jj<8; jj++ )
			{
				c  = ((unsigned char )pgm_read_word( t++  )) >> y;
				c += ((unsigned char )pgm_read_word( tt++  )) << (8-y);
				KS0108_Data( c );
			}

			t  =  &font[  (d[j+1] - ' ') * 8 + x ];
			tt =  &font[ (dd[j+1] - ' ') * 8 + x ];

			for ( char jj=0;jj<x; jj++ )
			{
				c  = ((unsigned char )pgm_read_word( t++  )) >> y;
				c += ((unsigned char )pgm_read_word( tt++  )) << (8-y);
				KS0108_Data( c );
			}

		}


	}
	

}

char vline[] = { 0, 1,3, 7, 15, 31, 63, 127 };

void KS0108_DrawVerticalLine( unsigned char x, unsigned char y1, unsigned char y2 )
{
	char dif2 = (y2>>3) - (y1>>3);

	for( char i=0;i< (y1>>3); i++ )
	{
		KS0108_SetAddr( x, i );
		KS0108_Data( 0 );
	}

	if (dif2==0)
	{
		KS0108_SetAddr( x, y1>>3 );
		if ( y1 == y2 )
		{
			KS0108_Data( 1 << ( y1 & 0x7 ) );
		}
		else
		{
			KS0108_Data( vline[ y2 & 0x7 ] & ( vline[ y1 & 0x7 ] ^ 255 ) );
		}
	}
	else 
	{
		KS0108_SetAddr( x, y1>>3 );
		KS0108_Data( vline[(y1&0x7)] ^ 255 );

		for( char i=(y1>>3) + 1;i< (y2>>3); i++ )
		{
			KS0108_SetAddr( x, i );
			KS0108_Data( 0xff );
		}

		KS0108_SetAddr( x, y2>>3 );
		KS0108_Data( vline[ y2 & 0x7 ] );
	}

	for( char i=(y2>>3)+1;i< 8; i++ )
	{
		KS0108_SetAddr( x, i );
		KS0108_Data( 0 );
	}

}

void KS0108_DrawVerticalLineWithBackground( unsigned char x, unsigned char y1, unsigned char y2, char *back )
{
	char dif2 = (y2>>3) - (y1>>3);

	for( char i=0;i< (y1>>3); i++ )
	{
		KS0108_SetAddr( x, i );
		KS0108_Data( *back++ );
	}

	if (dif2==0)
	{
		KS0108_SetAddr( x, y1>>3 );
		if ( y1 == y2 )
		{
			KS0108_Data( ( 1 << ( y1 & 0x7 ) ) | (*back++) );
		}
		else
		{
			KS0108_Data( ( vline[ y2 & 0x7 ] & ( vline[ y1 & 0x7 ] ^ 255 ) ) | (*back++) );
		}
	}
	else 
	{
		KS0108_SetAddr( x, y1>>3 );
		KS0108_Data( ( vline[(y1&0x7)] ^ 255 ) | (*back++) );

		for( char i=(y1>>3) + 1;i< (y2>>3); i++ )
		{
			KS0108_SetAddr( x, i );
			KS0108_Data( 0xff );
		}

		KS0108_SetAddr( x, y2>>3 );
		KS0108_Data( ( vline[ y2 & 0x7 ] ) | (*back++) );
	}

	for( char i=(y2>>3)+1;i< 8; i++ )
	{
		KS0108_SetAddr( x, i );
		KS0108_Data( *back++ );
	}

}

